package net.moonbiter.ebs.protocols.httpparams.tree.building;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import net.moonbiter.ReflectionException;
import net.moonbiter.ebs.NamingUtil;
import net.moonbiter.ebs.protocols.httpparams.tree.FileLeaf;
import net.moonbiter.ebs.protocols.httpparams.tree.Tree;
import net.moonbiter.ebs.validation.ValidationException;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;

public class UploadedFileTreeBuilder {
	private final static Logger logger = Logger.getLogger(UploadedFileTreeBuilder.class);
	
	// soglia oltre la quali i file inviati non verranno conservati
	// in memoria ma salvati su file temporanei in byte
	public final static int DEFAULT_SIZE_THRESHOLD = 1024^2;
	public final static int DEFAULT_MAX_SIZE = 1024^3;
	public final static String HIERARCHY_SEP = ".";
	
	private int sizeThreshold;
	private int maxSize;
	private File tmpDirectory;
	private ServletFileUpload upload;
	
	public UploadedFileTreeBuilder(File tmpDirectoryValue){
		this(tmpDirectoryValue,DEFAULT_SIZE_THRESHOLD,DEFAULT_MAX_SIZE);
	}
	
	public UploadedFileTreeBuilder(File tmpDirectoryValue, int sizeThresholdValue, int maxSizeValue){
		sizeThreshold = sizeThresholdValue;
		if (null==tmpDirectoryValue){
			throw new IllegalArgumentException("Param tmpDirectoryValue is null");
		}
		if (!tmpDirectoryValue.exists() || !tmpDirectoryValue.isDirectory()){
			throw new IllegalArgumentException("The indicated directory, \""
					+tmpDirectoryValue.getAbsolutePath()+"\" don't exists or is not a directory");
		}
		tmpDirectory = tmpDirectoryValue;
		this.maxSize = maxSizeValue;
		init();
	}
	
	private void init(){
		// file uplodati
		DiskFileItemFactory factory = new DiskFileItemFactory();
        // maximum size that will be stored in memory
        factory.setSizeThreshold(this.sizeThreshold);
        // the location for saving data that is larger than getSizeThreshold()
        factory.setRepository(this.tmpDirectory);

        upload = new ServletFileUpload(factory);
        // maximum size before a FileUploadException will be thrown
        upload.setSizeMax(this.maxSize);
	}
	
		
	/**
	 * Restituisce un mapping dei normali parametri GET/POST e
	 * dei file uplodati.
	 * 
	 * @param request
	 * @return
	 * @throws ReflectionException
	 * @throws ValidationException
	 */
	public void buildUploadedFileTree(HttpServletRequest request, Tree tree) throws ValidationException {		
        List<FileItem> allFileItems;
        logger.debug("Received a request with uploaded files? "+upload.isMultipartContent(request));
        if (!upload.isMultipartContent(request)){
        	return;
        }
		try {
			allFileItems = upload.parseRequest(request);
		} catch (FileUploadException e) {
			throw new ValidationException("Request not correct", e);
		}        
		
		FileItem[][] arrays = buildArrays(allFileItems);
        
        for (FileItem[] fileArrray : arrays){
        	String name = fileArrray[0].getFieldName();
        	tree.add(NamingUtil.getNodeNames(name), new FileLeaf(fileArrray));
        }
	}
	
	private FileItem[][] buildArrays(List<FileItem> allFileItems){
		Map<String,FileItem[]> arrays = new HashMap<String,FileItem[]>();
		for (FileItem fi : allFileItems){
			String name = fi.getFieldName();
			if (arrays.containsKey(name)){
				arrays.put(name,append(arrays.get(name),fi));
			} else {
				arrays.put(name,new FileItem[]{fi});
			}
		}
		return arrays.values().toArray(new FileItem[][]{});
	}
	
	private FileItem[] append(FileItem[] array, FileItem value){
		FileItem[] result = new FileItem[array.length+1];
		for (int i=0;i<array.length;i++){
			result[i] = array[i];
		}
		result[array.length] = value;
		return result;
	}
}
